/*P12_1.c: Programming PCD8544 GLCD via SPI with TI TIVA ARM*/
#include "tm4c123gh6pm.h"

#define RESET 0x20 /* PC5 reset pin */
#define DC    0x40 /* PC6 register select pin */

/* define the pixel size of display */
#define GLCD_WIDTH  84
#define GLCD_HEIGHT 48

void GLCD_setCursor(uint8_t x, uint8_t y);
void GLCD_clear(void);
void GLCD_init(void);
void GLCD_data_write(uint8_t data); 
void GLCD_command_write(uint8_t data);
void SSI2_init(void);
void SSI2_write(uint8_t data);
void delayMs(uint32_t n);
void delayUs(uint32_t n);


int main(void)
{
    /* sample font table */
    char font_table[][6] = {
        {0x7e, 0x11, 0x11, 0x11, 0x7e, 0},  /* A */
        {0x7f, 0x49, 0x49, 0x49, 0x36, 0},  /* B */
        {0x3e, 0x41, 0x41, 0x41, 0x22, 0}}; /* C */

    int i;
    
    GLCD_init();    /* initialize the GLCD controller */
    GLCD_clear();   /* clear display and home the cursor */

    /* display letter A */
    for (i = 0; i < 6; i++)
        GLCD_data_write(font_table[0][i]);

    /* display letter B */
    for (i = 0; i < 6; i++)
        GLCD_data_write(font_table[1][i]);

    /* display letter C */
    for (i = 0; i < 6; i++)
        GLCD_data_write(font_table[2][i]);
    
    while(1) { }
}

void GLCD_setCursor(uint8_t x, uint8_t y) 
{
    GLCD_command_write(0x80 | x);  /* column */
    GLCD_command_write(0x40 | y);  /* bank (8 rows per bank) */
}

/* clears the GLCD by writing zeros to the entire screen */
void GLCD_clear(void) 
{
    int32_t index;
    for (index = 0 ; index < (GLCD_WIDTH * GLCD_HEIGHT / 8) ; index++)
        GLCD_data_write(0x00);
    
    GLCD_setCursor(0, 0); /*After we clear the display, return to the home position */
}

void SSI2_init(void)
{    
    SYSCTL->RCGCSSI |= 0x04;    /* enable clock to SSI2 */
    SYSCTL->RCGCGPIO |= 0x02;   /* enable clock to GPIOB  */
    SYSCTL->RCGCGPIO |= 0x04;   /* enable clock to GPIOC */ 
 
    /* PORTB 7, 5, 4 for SSI2 */
    GPIOB->AFSEL |= 0xB0;       /* PORTB 7, 5, 4 for SSI2 */
    GPIOB->PCTL &= ~0xF0FF0000; /* PORTB 7, 5, 4 for SSI2 */
    GPIOB->PCTL |= 0x20220000;
    GPIOB->DEN |= 0xB0;         /* PORTB 7, 5, 4 as digital pins */

    /* PORTC 5, 6, 7 for CE, DC, Reset  */
    GPIOC->DATA |= 0x60;        /* set PORTC 5, 6, 7 idle high */
    GPIOC->DIR |= 0x60;         /* set PORTC 5, 6, 7 as output for CS */
    GPIOC->AMSEL &= ~0x60;      /* disable analog */
    GPIOC->DEN |= 0x60;         /* set PORTC 5, 6, 7 as digital pins */
    
    SSI2->CR1 = 0;      /* disable SSI2 and make it master */
    SSI2->CC = 0;       /* use system clock */
    SSI2->CPSR = 16;    /* clock prescaler divide by 16 gets 1 MHz clock */
    SSI2->CR0 = 0x0007; /* clock rate div by 1, phase/polarity 0 0, mode freescale, data size 8 */
    SSI2->CR1 = 2;      /* enable SSI2  */
}

/* send the initialization commands to PCD8544 GLCD controller */
void GLCD_init(void)
{
    SSI2_init();

    /* hardware reset of GLCD controller */
    GPIOC->DATA &= ~RESET;
    GPIOC->DATA |= RESET;

    GLCD_command_write(0x21); /* set extended command mode */
    GLCD_command_write(0xB0); /* set LCD Vop for contrast */
    GLCD_command_write(0x04); /* set temp coefficient */
    GLCD_command_write(0x14); /* set LCD bias mode 1:48 */
    GLCD_command_write(0x20); /* set normal command mode */
    GLCD_command_write(0x0C); /* set display normal mode */
}

/* write to GLCD controller data register */
void GLCD_data_write(uint8_t data) 
{
    /* select data register */
    GPIOC->DATA |= DC;
        
    /* send data via SSI */
    SSI2_write(data);
}

/* write to GLCD controller command register */
void GLCD_command_write(uint8_t data)
{
    /* select command register */
    GPIOC->DATA &= ~DC;
        
    /* send data via SSI */
    SSI2_write(data);
}

void SSI2_write(uint8_t data)
{
    SSI2->DR = data;            /* write data */
    while (SSI2->SR & 0x10) ;   /* wait for transmit done */
    data = SSI2->DR;
}

/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(uint32_t n)
{
    uint32_t i, j;
    for(i = 0; i < n; i++)
        for(j = 0; j < 3180; j++)
            {} /* do nothing for 1 ms */
}

/* delay n microseconds (16 MHz CPU clock) */
void delayUs(uint32_t n)
{
    uint32_t i, j;
    for(i = 0 ; i < n; i++)
        for(j = 0; j < 3; j++)
            {}  /* do nothing for 1 ms */
}

/* This function is called by the startup assembly code to perform system specific initialization tasks. */
void SystemInit(void)
{
    /* Grant coprocessor access */
    /* This is required since TM4C123G has a floating point coprocessor */
    SCB->CPACR |= 0x00f00000;
}